/* 
 * File:   Conexion.cpp
 * Author: max
 * 
 * Created on October 20, 2012, 9:03 AM
 */
#include <iostream>
#include <stdlib.h>
#include <errno.h>
#include "Conexion.h"
#include "ProtocoloConstantes.h"

using namespace TP::Comunicacion;

#define BUF_SIZE 500
#define CONEXION_TIMEOUT 1

Conexion::Conexion(const string& pIp, const string& pPuerto)
{
    this->ip = pIp;
    this->puerto = pPuerto;
}

Conexion::~Conexion() {}

void Conexion::Iniciar() throw (Excepcion&){
    
    try
    {
        if ( -1 == (this->sockfd = socket(AF_INET, SOCK_STREAM, 0) ))
        {
            string msg = "No se puede iniciar el socket. ";
            msg += strerror(errno);
            throw Excepcion ( msg);
        }
        
        struct sockaddr_in dest_addr;
        
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(atoi(this->puerto.c_str()));
        dest_addr.sin_addr.s_addr = inet_addr(this->ip.c_str());
        memset(&(dest_addr.sin_zero), '\0', 8);

        if (connect (this->sockfd, (struct sockaddr*) &dest_addr, sizeof (struct sockaddr)) == -1)
        {
            throw Excepcion ("No se puede conectar al host " 
                             + this->ip 
                             + " puerto " 
                             + this->puerto 
                             + ". "
                             + strerror (errno) );
        }
    }
    catch (Excepcion& pEx)
    {
        if (-1 != this->sockfd)
            shutdown (this->sockfd, SHUT_RDWR);
        
        pEx.expandirMensaje("No se puede iniciar la conexion. ");
        throw;
    }
    
}

void Conexion::Finalizar() throw (Excepcion&){
    
    try
    {
        // Se finaliza la conexion.
        close(this->sockfd);
    }
    catch (Excepcion& pEx)
    {
        pEx.expandirMensaje("No se puede finalizar la conexion.");
        throw;
    }
}

int Conexion::Escribir(const string& pMensaje) throw (Excepcion&){
    
    int cntBytes = -1;
    
    if (this->ConexionPerdida)
        throw Excepcion ("Conexion perdida.");
    
    try
    {
        /*
         * Toma el mensaje, y lo escribe a traves de la conexion.
         */
        string msg_env = pMensaje;
        
        // Finalizo el mensaje para transmision.
        msg_env += FIN_MENSAJE;

        // Envio el mensaje.
        cntBytes = send (this->sockfd, msg_env.c_str(), msg_env.size(), 0);
        
        if (cntBytes <= 0)
            throw Excepcion ("Fallo de comunicacion con el servidor.");
    }
    catch (Excepcion& pEx)
    {
        pEx.expandirMensaje("No se puede escribir el mensaje.");
        throw;
    }
    
    return cntBytes;
}

int Conexion::Leer (string& pMensaje) throw (Excepcion&){

    try
    {
        /* Llegado el caso, aca es donde se tiene que encodear el mensaje para el envio.
         *      
         */
        string mensaje = "";
        char buffer [BUF_SIZE];
        bzero(buffer,BUF_SIZE);
        int bytes_recv = -1;
        int intentos = 10;

        while (true)
        {
            // Inicializo el buffer.
            bzero(buffer,BUF_SIZE);

            bytes_recv = recv(this->sockfd, buffer, 255, 0);

            if (bytes_recv <= 0)
            {
                intentos--;
                if (intentos == 0)
                    throw CONEXION_TIMEOUT;
                else 
                    sleep (1);
            }
            else
            {
                // Vuelco el buffer en el mensaje.
                for (int i = 0; i < bytes_recv; i++){

                    if (FIN_MENSAJE != buffer[i])
                        mensaje += buffer[i];
                    else
                    {
                        // Decodifico el mensaje 
                        for (unsigned int i = 0; i < mensaje.size(); i++)
                            if (SALTO_LINEA == mensaje[i])
                                mensaje [i] = '\n';

                        // Notifico a los manejadores de mensaje entrante.

                        pMensaje =  mensaje;        
                        return pMensaje.size();
                    }
                }
            }
        } // while
    }
    catch (int pEx)
    {
        if (CONEXION_TIMEOUT)
            throw Excepcion ("Conexion Timeout");
    }
    catch (Excepcion& pEx)
    {
        pEx.expandirMensaje("No se puede leer el mensaje desde el servidor.");
        throw;
    }
}


